/* Copyright (c) 2020 XEPIC Corporation Limited */
/*
 * Copyright (c) 1998-2019 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

#include "config.h"

/*
 * This file contains all the dump methods of the netlist classes.
 */
#include <iomanip>
#include <iostream>
#include <typeinfo>

#include "PExpr.h"
#include "compiler.h"
#include "discipline.h"
#include "ivl_assert.h"
#include "netclass.h"
#include "netdarray.h"
#include "netlist.h"
#include "netqueue.h"
#include "netvector.h"

static ostream& operator<<(ostream& o, NetBlock::Type t) {
  switch (t) {
    case NetBlock::SEQU:
      o << "begin";
      break;
    case NetBlock::PARA:
      o << "fork";
      break;
    case NetBlock::PARA_JOIN_NONE:
      o << "fork-join_none";
      break;
    case NetBlock::PARA_JOIN_ANY:
      o << "fork-join_any";
      break;
  }
  return o;
}

ostream& operator<<(ostream& o, ivl_drive_t str) {
  switch (str) {
    case IVL_DR_HiZ:
      o << "highz";
      break;
    case IVL_DR_SMALL:
      o << "small";
      break;
    case IVL_DR_MEDIUM:
      o << "medium";
      break;
    case IVL_DR_WEAK:
      o << "weak";
      break;
    case IVL_DR_LARGE:
      o << "large";
      break;
    case IVL_DR_PULL:
      o << "pull";
      break;
    case IVL_DR_STRONG:
      o << "strong";
      break;
    case IVL_DR_SUPPLY:
      o << "supply";
      break;
    default:
      assert(0);
  }
  return o;
}

ostream& operator<<(ostream& o, ivl_variable_type_t val) {
  switch (val) {
    case IVL_VT_VOID:
      o << "void";
      break;
    case IVL_VT_NO_TYPE:
      o << "<no_type>";
      break;
    case IVL_VT_REAL:
      o << "real";
      break;
    case IVL_VT_BOOL:
      o << "bool";
      break;
    case IVL_VT_LOGIC:
      o << "logic";
      break;
    case IVL_VT_STRING:
      o << "string";
      break;
    case IVL_VT_DARRAY:
      o << "darray";
      break;
    case IVL_VT_CLASS:
      o << "class";
      break;
    case IVL_VT_QUEUE:
      o << "queue";
      break;
  }
  return o;
}

ostream& operator<<(ostream& o, ivl_switch_type_t val) {
  switch (val) {
    case IVL_SW_TRAN:
      o << "tran";
      break;
    case IVL_SW_TRANIF0:
      o << "tranif0";
      break;
    case IVL_SW_TRANIF1:
      o << "tranif1";
      break;
    case IVL_SW_RTRAN:
      o << "rtran";
      break;
    case IVL_SW_RTRANIF0:
      o << "rtranif0";
      break;
    case IVL_SW_RTRANIF1:
      o << "rtranif1";
      break;
    case IVL_SW_TRAN_VP:
      o << "tran(VP)";
      break;
  }
  return o;
}

ostream& operator<<(ostream& fd, PortType::Enum val) {
  switch (val) {
    case PortType::NOT_A_PORT:
      fd << "NOT_A_PORT";
      break;
    case PortType::PIMPLICIT:
      fd << "PIMPLICIT";
      break;
    case PortType::PINPUT:
      fd << "PINPUT";
      break;
    case PortType::POUTPUT:
      fd << "POUTPUT";
      break;
    case PortType::PINOUT:
      fd << "PINOUT";
      break;
    case PortType::PREF:
      fd << "PREF";
      break;
    default:
      fd << "PortType::Enum::?";
      break;
  }

  return fd;
}

ostream& operator<<(ostream& fd, NetCaseCmp::kind_t that) {
  switch (that) {
    case NetCaseCmp::EEQ:
      fd << "===";
      break;
    case NetCaseCmp::NEQ:
      fd << "!==";
      break;
    case NetCaseCmp::WEQ:
      fd << "==?";
      break;
    case NetCaseCmp::WNE:
      fd << "!=?";
      break;
    case NetCaseCmp::XEQ:
      fd << "==x?";
      break;
    case NetCaseCmp::ZEQ:
      fd << "==z?";
      break;
  }
  return fd;
}

ostream& ivl_type_s::debug_dump(ostream& o) const {
  o << typeid(*this).name();
  return o;
}

ostream& netclass_t::debug_dump(ostream& fd) const {
  fd << "class " << name_ << "{";
  for (size_t idx = 0; idx < property_table_.size(); idx += 1) {
    if (idx != 0) fd << "; ";
    if (property_table_[idx].type)
      property_table_[idx].type->debug_dump(fd);
    else
      fd << "NO_TYPE";
    fd << " " << property_table_[idx].name;
  }
  fd << "}";
  return fd;
}

ostream& netdarray_t::debug_dump(ostream& o) const {
  o << "dynamic array of " << *element_type();
  return o;
}

ostream& netqueue_t::debug_dump(ostream& fd) const {
  fd << "queue of " << *element_type();
  return fd;
}

ostream& netvector_t::debug_dump(ostream& o) const {
  o << "netvector_t:" << type_ << (signed_ ? " signed" : " unsigned")
    << packed_dims_;
  return o;
}

static inline void dump_scope_path(ostream& o, const NetScope* scope) {
  const NetScope* parent = scope->parent();
  if (parent) {
    dump_scope_path(o, parent);
    o << ".";
  }
  o << scope->fullname();
}

ostream& operator<<(ostream& o, struct __ScopePathManip marg) {
  if (marg.scope != 0) dump_scope_path(o, marg.scope);
  return o;
}

ostream& operator<<(ostream& o, struct __ObjectPathManip marg) {
  if (marg.obj != 0) {
    dump_scope_path(o, marg.obj->scope());
    o << "." << marg.obj->name();
  }
  return o;
}

ostream& operator<<(ostream& fd, Link::DIR dir) {
  switch (dir) {
    case Link::PASSIVE:
      fd << "PASSIVE";
      break;
    case Link::INPUT:
      fd << "INPUT";
      break;
    case Link::OUTPUT:
      fd << "OUTPUT";
      break;
    default:
      fd << "<" << (int)dir << ">";
      break;
  }
  return fd;
}

void NetPins::show_type(ostream& fd) const { fd << typeid(*this).name(); }

void NetObj::show_type(ostream& fd) const {
  fd << typeid(*this).name() << "[" << scope_path(scope_) << "." << name_
     << "]";
}

struct __ShowTypeManip {
  const NetPins* pins;
};
inline __ShowTypeManip show_type(const NetPins* pins) {
  __ShowTypeManip tmp;
  tmp.pins = pins;
  return tmp;
}

inline ostream& operator<<(ostream& fd, __ShowTypeManip man) {
  if (man.pins == 0)
    fd << "NexusSet";
  else
    man.pins->show_type(fd);
  return fd;
}

void Link::dump_link(ostream& fd, unsigned ind) const {
  const Link* cur;
  const Nexus* nex = nexus();

  if (nex == 0) {
    fd << setw(ind) << ""
       << "<unlinked>" << endl;
    return;
  }

  for (cur = nex->first_nlink(); cur; cur = cur->next_nlink()) {
    const NetPins* obj = cur->get_obj();
    unsigned pin = cur->get_pin();
    fd << setw(ind) << ""
       << "Pin " << pin << " of " << show_type(obj) << ", dir=" << cur->dir_
       << endl;
  }
}

void NetBranch::dump(ostream& o, unsigned ind) const {
  static const char* pin_names[2] = {"terminal0", "terminal1"};

  o << setw(ind) << ""
    << "branch island=" << get_island();
  o << " // " << get_fileline() << endl;
  dump_node_pins(o, ind + 4, pin_names);
}

void NetDelaySrc::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "specify delay";
  if (posedge_) o << " posedge";
  if (negedge_) o << " negedge";
  if (is_condit()) {
    if (has_condit())
      o << " if";
    else
      o << " ifnone";
  }
  o << " src "
    << "(" << transition_delays_[IVL_PE_01] << ","
    << transition_delays_[IVL_PE_10] << "," << transition_delays_[IVL_PE_0z]
    << "/" << transition_delays_[IVL_PE_z1] << ","
    << transition_delays_[IVL_PE_1z] << "," << transition_delays_[IVL_PE_z0]
    << "/" << transition_delays_[IVL_PE_0x] << ","
    << transition_delays_[IVL_PE_x1] << "," << transition_delays_[IVL_PE_1x]
    << "/" << transition_delays_[IVL_PE_x0] << ","
    << transition_delays_[IVL_PE_xz] << "," << transition_delays_[IVL_PE_zx]
    << ") scope=" << scope_path(scope()) << endl;
  dump_node_pins(o, ind + 4);
}

static inline ostream& operator<<(ostream& out, const netrange_t& that) {
  if (that.defined())
    out << "[" << that.get_msb() << ":" << that.get_lsb() << "]";
  else
    out << "[]";

  return out;
}

ostream& operator<<(ostream& out, const list<netrange_t>& rlist) {
  for (list<netrange_t>::const_iterator cur = rlist.begin(); cur != rlist.end();
       ++cur) {
    out << *cur;
  }
  return out;
}

ostream& operator<<(ostream& out, const vector<netrange_t>& rlist) {
  for (vector<netrange_t>::const_iterator cur = rlist.begin();
       cur != rlist.end(); ++cur) {
    out << *cur;
  }
  return out;
}

/* Dump a net. This can be a wire or register. */
void NetNet::dump_net(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << type() << ": " << name() << unpacked_dims_
    << " unpacked dims=" << unpacked_dimensions();
  o << " pin_count=" << pin_count();
  if (local_flag_) o << " (local)";
  switch (port_type_) {
    case NetNet::NOT_A_PORT:
      break;
    case NetNet::PIMPLICIT:
      o << " implicit-port?";
      break;
    case NetNet::PINPUT:
      o << " input";
      break;
    case NetNet::POUTPUT:
      o << " output";
      break;
    case NetNet::PINOUT:
      o << " inout";
      break;
    case NetNet::PREF:
      o << " ref";
      break;
  }

  if (ivl_discipline_t dis = get_discipline())
    o << " discipline=" << dis->name();

  if (net_type_) o << " " << *net_type_;

  o << " (eref=" << peek_eref() << ", lref=" << peek_lref() << ")";
  if (scope()) o << " scope=" << scope_path(scope());
  o << " #(" << rise_time() << "," << fall_time() << "," << decay_time()
    << ") vector_width=" << vector_width() << " pin_count=" << pin_count();
  if (pins_are_virtual()) {
    o << " pins_are_virtual" << endl;
    return;
  }
  o << endl;

  for (unsigned idx = 0; idx < pin_count(); idx += 1) {
    if (!pin(idx).is_linked()) continue;

    const Nexus* nex = pin(idx).nexus();
    o << setw(ind + 4) << ""
      << "[" << idx << "]: " << nex << " " << nex->name() << endl;
  }

  for (unsigned idx = 0; idx < delay_paths_.size(); idx += 1) {
    const NetDelaySrc* cur = delay_paths_[idx];
    cur->dump(o, ind + 4);
  }

  dump_obj_attr(o, ind + 4);
}

/* Dump a NetNode and its pins. Dump what I know about the netnode on
   the first line, then list all the pins, with the name of the
   connected signal. */
void NetNode::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "node: ";
  o << typeid(*this).name() << " #(" << rise_time() << "," << fall_time() << ","
    << decay_time() << ") " << name() << endl;

  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

/* This is the generic dumping of all the signals connected to each
   pin of the object. The "this" object is not printed, only the
   signals connected to this. */
void NetPins::dump_node_pins(ostream& o, unsigned ind,
                             const char** pin_names) const {
  for (unsigned idx = 0; idx < pin_count(); idx += 1) {
    o << setw(ind) << "" << idx;
    if (pin_names && pin_names[idx])
      o << " " << pin_names[idx];
    else
      o << " pin" << idx;

    switch (pin(idx).get_dir()) {
      case Link::PASSIVE:
        o << " p";
        break;
      case Link::INPUT:
        o << " I";
        break;
      case Link::OUTPUT:
        o << " O";
        break;
    }

    o << " (" << pin(idx).drive0() << "0 " << pin(idx).drive1() << "1): ";

    if (pin(idx).is_linked()) {
      const Nexus* nex = pin(idx).nexus();
      o << nex << " " << nex->name();
    }
    o << endl;
  }
}

void NetObj::dump_obj_attr(ostream& o, unsigned ind) const {
  unsigned idx;
  for (idx = 0; idx < attr_cnt(); idx += 1) {
    o << setw(ind) << "" << attr_key(idx) << " = \"" << attr_value(idx) << "\""
      << endl;
  }
}

void NetAbs::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Absolute value (NetAbs): " << name() << " width=" << width()
    << " pin_count=" << pin_count() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetAddSub::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Adder (NetAddSub): " << name() << " width=" << width()
    << " pin_count=" << pin_count() << endl;
  static const char* pin_names[] = {"Cout  ", "DataA ", "DataB ", "Result"};

  dump_node_pins(o, ind + 4, pin_names);
  dump_obj_attr(o, ind + 4);
}

void NetArrayDq::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NetArrayDq: " << name() << " array=" << mem_->name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetCastInt2::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Cast to int2. (NetCastInt2): " << name() << " width=" << width()
    << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetCastInt4::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Cast to int4. (NetCastInt4): " << name() << " width=" << width()
    << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetCastReal::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Cast to real (NetCastReal): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetCLShift::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Combinatorial shift (NetCLShift): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetCompare::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "LPM_COMPARE (NetCompare " << (get_signed() ? "signed" : "unsigned")
    << "): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetConcat::dump_node(ostream& o, unsigned ind) const {
  if (transparent_)
    o << setw(ind) << ""
      << "NetConcat8: ";
  else
    o << setw(ind) << ""
      << "NetConcat: ";
  o << name();

  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  else
    o << " #(0,0,0)";
  o << " scope=" << scope_path(scope()) << " width=" << width_ << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetDivide::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NET_DIVIDE (NetDivide): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetMult::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "LPM_MULT (NetMult): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetPow::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "LPM_POW (NetPow): " << name() << " scope=" << scope_path(scope())
    << " delay=(";
  if (rise_time())
    o << *rise_time() << "," << *fall_time() << "," << *decay_time();
  else
    o << "0,0,0";

  o << ")" << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetMux::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "Multiplexer (NetMux): " << name() << " width=" << width_
    << " swidth=" << swidth_ << " size=" << size_
    << " scope=" << scope_path(scope()) << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetBUFZ::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NetBUFZ: " << name() << " scope=" << scope_path(scope()) << " delay=("
    << rise_time() << "," << fall_time() << "," << decay_time()
    << ") width=" << width() << (transparent() ? " " : " non-") << "transparent"
    << endl;
  dump_node_pins(o, ind + 4);
}

void NetCaseCmp::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "case compare " << kind_ << ": " << name() << endl;

  dump_node_pins(o, ind + 4);
}

void NetConst::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "constant " << value_;
  o << ": " << name();
  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  else
    o << " #(.,.,.)";
  o << endl;
  dump_node_pins(o, ind + 4);
}

void NetFF::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "LPM_FF: " << name() << " scope=" << scope_path(scope());
  if (negedge_)
    o << " negedge";
  else
    o << " posedge";
  o << " aset_value=" << aset_value_ << endl;

  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetLatch::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "LPM_LATCH: " << name() << " scope=" << scope_path(scope()) << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetLiteral::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "constant real " << real_ << ": " << name();
  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  else
    o << " #(.,.,.)";
  o << endl;
  dump_node_pins(o, ind + 4);
}

void NetLogic::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "logic: ";
  switch (type_) {
    case AND:
      o << "and";
      break;
    case BUF:
      o << "buf";
      break;
    case BUFIF0:
      o << "bufif0";
      break;
    case BUFIF1:
      o << "bufif1";
      break;
    case CMOS:
      o << "cmos";
      break;
    case NAND:
      o << "nand";
      break;
    case NMOS:
      o << "nmos";
      break;
    case NOR:
      o << "nor";
      break;
    case NOT:
      o << "not";
      break;
    case NOTIF0:
      o << "notif0";
      break;
    case NOTIF1:
      o << "notif1";
      break;
    case OR:
      o << "or";
      break;
    case PULLDOWN:
      o << "pulldown";
      break;
    case PULLUP:
      o << "pullup";
      break;
    case RCMOS:
      o << "rcmos";
      break;
    case RNMOS:
      o << "rnmos";
      break;
    case RPMOS:
      o << "rpmos";
      break;
    case PMOS:
      o << "pmos";
      break;
    case XNOR:
      o << "xnor";
      break;
    case XOR:
      o << "xor";
      break;
  }
  o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") "
    << name() << " scope=" << scope_path(scope()) << endl;

  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetModulo::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NET_MODULO (NetModulo): " << name() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetPartSelect::dump_node(ostream& o, unsigned ind) const {
  const char* pt = "";
  switch (dir_) {
    case VP:
      pt = "VP";
      break;
    case PV:
      pt = "PV";
      break;
  }
  o << setw(ind) << ""
    << "NetPartSelect(" << pt << "): " << name();
  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  else
    o << " #(.,.,.)";
  o << " off=" << off_ << " wid=" << wid_ << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetSubstitute::dump_node(ostream& fd, unsigned ind) const {
  fd << setw(ind) << ""
     << "NetSubstitute: " << name();
  if (rise_time())
    fd << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
       << ")";
  else
    fd << " #(.,.,.)";
  fd << " width=" << wid_ << " base=" << off_ << endl;
  dump_node_pins(fd, ind + 4);
  dump_obj_attr(fd, ind + 4);
}

void NetReplicate::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NetReplicate: " << name() << " wid=" << width_
    << ", repeat_=" << repeat_ << ", input wid=" << width_ / repeat_ << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetSignExtend::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "NetSignExtend: " << name();
  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  else
    o << " #(.,.,.)";
  o << " output width=" << width_ << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetUReduce::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "reduction logic: ";
  switch (type_) {
    case NONE:
      o << "NONE";
      break;
    case AND:
      o << "and";
      break;
    case OR:
      o << "or";
      break;
    case XOR:
      o << "xor";
      break;
    case NAND:
      o << "nand";
      break;
    case NOR:
      o << "nor";
      break;
    case XNOR:
      o << "xnor";
      break;
  }
  o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") "
    << name() << " scope=" << scope_path(scope()) << endl;

  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetSysFunc::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << def_->name << "(...) -->" << data_type()
    << " width=" << vector_width() << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetUserFunc::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "USER FUNC: " << scope_path(def_);
  if (rise_time())
    o << " #(" << *rise_time() << "," << *fall_time() << "," << *decay_time()
      << ")";
  o << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetTaskDef::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "task " << scope_path(scope()) << ";" << endl;

  for (unsigned idx = 0; idx < port_count(); idx += 1) {
    const NetNet* pnet = port(idx);
    o << setw(ind + 4) << "";
    assert(pnet);
    switch (pnet->port_type()) {
      case NetNet::PINPUT:
        o << "input ";
        break;
      case NetNet::POUTPUT:
        o << "output ";
        break;
      case NetNet::PINOUT:
        o << "input ";
        break;
      default:
        o << "NOT_A_PORT ";
        break;
    }
    o << pnet->name() << ";" << endl;
  }

  if (proc_)
    proc_->dump(o, ind + 4);
  else
    o << setw(ind + 4) << ""
      << "MISSING PROCEDURAL CODE" << endl;

  o << setw(ind) << ""
    << "endtask" << endl;
}

void NetTran::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << type_ << " " << name() << " island " << get_island();
  if (type_ == IVL_SW_TRAN_VP) {
    o << " width=" << vector_width() << " part=" << part_width()
      << " offset=" << part_offset();
  }
  o << " delay=(";
  if (rise_time())
    o << *rise_time() << "," << *fall_time() << "," << *decay_time();
  else
    o << "0,0,0";

  o << ")" << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetUDP::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "UDP (" << udp_name() << "): ";
  o << " #(" << rise_time() << "," << fall_time() << "," << decay_time() << ") "
    << name() << endl;

  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetProcTop::dump(ostream& o, unsigned ind) const {
  switch (type_) {
    case IVL_PR_INITIAL:
      o << "initial  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
    case IVL_PR_ALWAYS:
      o << "always  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
    case IVL_PR_ALWAYS_COMB:
      o << "always_comb  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
    case IVL_PR_ALWAYS_FF:
      o << "always_ff  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
    case IVL_PR_ALWAYS_LATCH:
      o << "always_latch  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
    case IVL_PR_FINAL:
      o << "final  /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
  }

  for (unsigned idx = 0; idx < attr_cnt(); idx += 1) {
    o << setw(ind + 2) << ""
      << "(* " << attr_key(idx) << " = " << attr_value(idx) << " *)" << endl;
  }

  statement_->dump(o, ind + 2);
}

void NetAnalogTop::dump(ostream& o, unsigned ind) const {
  switch (type_) {
    case IVL_PR_INITIAL:
      o << "analog initial /* " << get_fileline() << " in "
        << scope_path(scope_) << " */" << endl;
      break;

    case IVL_PR_ALWAYS:
      o << "analog /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;

      // These are not used in an analog context.
    case IVL_PR_ALWAYS_COMB:
    case IVL_PR_ALWAYS_FF:
    case IVL_PR_ALWAYS_LATCH:
      assert(0);
      break;

    case IVL_PR_FINAL:
      o << "analog final /* " << get_fileline() << " in " << scope_path(scope_)
        << " */" << endl;
      break;
  }

  statement_->dump(o, ind + 2);
}

void NetAlloc::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "// allocate storage : " << scope_path(scope_) << endl;
}

void NetAssign_::dump_lval(ostream& o) const {
  if (sig_)
    o << sig_->name();
  else if (nest_) {
    o << "(";
    nest_->dump_lval(o);
    o << ")";
  } else
    o << "<?>";

  if (!member_.nil()) {
    o << "." << member_;
  }
  if (word_) {
    o << "[word=" << *word_ << "]";
  }
  if (base_) {
    o << "[" << *base_ << " +: " << lwid_ << "]";
  }
}

void NetAssignBase::dump_lval(ostream& o) const {
  o << "{";
  lval_->dump_lval(o);

  for (NetAssign_* cur = lval_->more; cur; cur = cur->more) {
    o << ", ";
    cur->dump_lval(o);
  }

  o << "}";
}

/* Dump an assignment statement */
void NetAssign::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "";
  dump_lval(o);

  if (op_)
    o << " " << op_ << "= ";
  else
    o << " = ";

  if (const NetExpr* de = get_delay()) o << "#(" << *de << ") ";

  o << *rval() << ";" << endl;
}

void NetAssignNB::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "";
  dump_lval(o);

  o << " <= ";

  if (const NetExpr* de = get_delay()) o << "#(" << *de << ") ";
  if (count_) o << "repeat(" << *count_ << ") ";
  if (event_) {
    o << *event_;
  }

  o << *rval() << ";" << endl;
}

void NetAssignBase::dump(ostream& o, unsigned ind) const {
  if (const NetAssignNB* n1 = dynamic_cast<const NetAssignNB*>(this)) {
    n1->dump(o, ind);
  } else if (const NetAssign* n2 = dynamic_cast<const NetAssign*>(this)) {
    n2->dump(o, ind);
  }
}

/* Dump a block statement */
void NetBlock::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << type_;
  if (subscope_) o << " : " << scope_path(subscope_);
  o << endl;

  if (last_) {
    const NetProc* cur = last_;
    do {
      cur = cur->next_;
      cur->dump(o, ind + 4);
    } while (cur != last_);
  }

  o << setw(ind) << ""
    << "end" << endl;
}

void NetCase::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "";
  switch (quality_) {
    case IVL_CASE_QUALITY_BASIC:
      break;
    case IVL_CASE_QUALITY_UNIQUE:
      o << "unique ";
      break;
    case IVL_CASE_QUALITY_UNIQUE0:
      o << "unique0 ";
      break;
    case IVL_CASE_QUALITY_PRIORITY:
      o << "priority ";
      break;
  }
  switch (type_) {
    case EQ:
      o << "case (" << *expr_ << ")" << endl;
      break;
    case EQX:
      o << "casex (" << *expr_ << ")" << endl;
      break;
    case EQZ:
      o << "casez (" << *expr_ << ")" << endl;
      break;
  }

  for (unsigned idx = 0; idx < items_.size(); idx += 1) {
    o << setw(ind + 2) << "";
    if (items_[idx].guard)
      o << *items_[idx].guard << ":";
    else
      o << "default:";

    if (items_[idx].statement) {
      o << endl;
      items_[idx].statement->dump(o, ind + 6);
    } else {
      o << " ;" << endl;
    }
  }

  o << setw(ind) << ""
    << "endcase" << endl;
}

void NetCAssign::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "cassign ";
  dump_lval(o);
  o << " = " << *rval() << "; /* " << get_fileline() << " */" << endl;
}

void NetCondit::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "if (";
  expr_->dump(o);
  o << ")" << endl;

  if (if_)
    if_->dump(o, ind + 4);
  else
    o << setw(ind + 4) << ""
      << "/* empty */ ;" << endl;

  if (else_) {
    o << setw(ind) << ""
      << "else" << endl;
    else_->dump(o, ind + 4);
  }
}

void NetContribution::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "";
  lval_->dump(o);
  o << " <+ ";
  rval_->dump(o);
  o << ";" << endl;
}

void NetDeassign::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "deassign ";
  dump_lval(o);
  o << "; /* " << get_fileline() << " */" << endl;
}

void NetDisable::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "disable ";
  if (target_)
    o << scope_path(target_);
  else
    o << "fork";
  o << "; "
    << "/* " << get_fileline() << " */" << endl;
}

void NetDoWhile::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "do" << endl;
  proc_->dump(o, ind + 3);
  o << setw(ind) << ""
    << "while (" << *cond_ << ");" << endl;
}

void NetEvProbe::dump_node(ostream& o, unsigned ind) const {
  o << setw(ind) << "";

  switch (edge_) {
    case ANYEDGE:
      o << "anyedge ";
      break;
    case POSEDGE:
      o << "posedge ";
      break;
    case NEGEDGE:
      o << "negedge ";
      break;
  }
  o << setw(ind) << ""
    << "-> " << event_->name() << "; " << endl;
  dump_node_pins(o, ind + 4);
  dump_obj_attr(o, ind + 4);
}

void NetEvTrig::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "-> " << event_->name() << "; "
    << "// " << get_fileline() << endl;
}

void NetEvWait::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "";

  /* Check for a wait fork. */
  if ((nevents() == 1) && (event(0) == 0)) {
    o << "wait fork;";
    return;
  }

  o << "@(";

  if (nevents() > 0) o << event(0)->name();

  for (unsigned idx = 1; idx < nevents(); idx += 1)
    o << " or " << event(idx)->name();

  o << ")  // " << get_fileline() << endl;

  if (statement_)
    statement_->dump(o, ind + 2);
  else
    o << setw(ind + 2) << ""
      << "/* noop */ ;" << endl;
}

ostream& operator<<(ostream& out, const NetEvWait& obj) {
  obj.dump_inline(out);
  return out;
}

void NetEvWait::dump_inline(ostream& o) const {
  /* Check for a wait fork. */
  if ((nevents() == 1) && (event(0) == 0)) {
    o << "wait fork;";
    return;
  }

  o << "@(";

  if (nevents() > 0) o << event(0)->name();

  for (unsigned idx = 1; idx < nevents(); idx += 1)
    o << " or " << event(idx)->name();

  o << ") ";
}

void NetForce::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "force ";
  dump_lval(o);
  o << " = " << *rval() << "; /* " << get_fileline() << " */" << endl;
}

void NetForever::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "forever" << endl;
  statement_->dump(o, ind + 2);
}

void NetForLoop::dump(ostream& fd, unsigned ind) const {
  fd << setw(ind) << ""
     << "FOR LOOP index=" << index_->name() << endl;
  statement_->dump(fd, ind + 4);
  step_statement_->dump(fd, ind + 4);
}

void NetFree::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "// free storage : " << scope_path(scope_) << endl;
}

void NetFuncDef::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "function definition for " << scope_path(scope()) << endl;
  if (result_sig_) {
    o << setw(ind + 2) << ""
      << "Return signal: ";
    if (result_sig_->get_signed()) o << "+";
    o << result_sig_->name() << endl;
  }
  o << setw(ind + 2) << ""
    << "Arguments: ";
  if (port_count() == 0) o << "<none>";
  o << endl;
  for (unsigned idx = 0; idx < port_count(); idx += 1) {
    o << setw(ind + 4) << ""
      << "Arg[" << idx + 1 << "] = ";
    switch (port(idx)->port_type()) {
      default:
        o << "implicit-port? ";
        break;
      case NetNet::PINPUT:
        o << "input ";
        break;
      case NetNet::POUTPUT:
        o << "output ";
        break;
      case NetNet::PINOUT:
        o << "inout ";
        break;
    }
    if (port(idx)->get_signed()) o << "+";
    o << port(idx)->name() << endl;
  }
  if (proc_)
    proc_->dump(o, ind + 2);
  else
    o << setw(ind + 2) << ""
      << "MISSING PROCEDURAL CODE" << endl;
}

void NetPDelay::dump(ostream& o, unsigned ind) const {
  if (expr_) {
    o << setw(ind) << ""
      << "#" << *expr_;

  } else {
    o << setw(ind) << ""
      << "#" << delay_;
  }

  if (statement_) {
    o << endl;
    statement_->dump(o, ind + 2);
  } else {
    o << " /* noop */;" << endl;
  }
}

void NetRelease::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "release ";
  dump_lval(o);
  o << "; /* " << get_fileline() << " */" << endl;
}

void NetRepeat::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "repeat (" << *expr_ << ")" << endl;
  statement_->dump(o, ind + 2);
}

void netclass_t::dump_scope(ostream& fd) const { class_scope_->dump(fd); }

void NetScope::dump(ostream& o) const {
  /* This is a constructed hierarchical name. */
  o << scope_path(this) << " ";

  print_type(o);
  if (is_auto()) o << " (automatic)";
  if (is_cell()) o << " (cell)";
  if (nested_module()) o << " (nested)";
  if (program_block()) o << " (program)";
  if (is_interface()) o << " (interface)";
  o << " " << children_.size() << " children, " << classes_.size() << " classes"
    << endl;
  if (unit() && !is_unit())
    o << "    in compilation unit " << unit()->basename() << endl;

  for (unsigned idx = 0; idx < attr_cnt(); idx += 1)
    o << "    (* " << attr_key(idx) << " = " << attr_value(idx) << " *)"
      << endl;

  o << "    timescale = 10e" << time_unit() << " / 10e" << time_precision()
    << endl;

  /* Dump the parameters for this scope. */
  {
    map<perm_string, param_expr_t>::const_iterator pp;
    for (pp = parameters.begin(); pp != parameters.end(); ++pp) {
      if ((*pp).second.is_annotatable)
        o << "    specparam ";
      else
        o << "    parameter ";

      o << pp->second.type << " ";

      if ((*pp).second.signed_flag) o << "signed ";

      if ((*pp).second.msb)
        o << "[" << *(*pp).second.msb << ":" << *(*pp).second.lsb << "] ";

      o << (*pp).first << " = ";
      if (pp->second.val)
        o << *(*pp).second.val;
      else
        o << "<nil>";

      for (range_t* ran = (*pp).second.range; ran; ran = ran->next) {
        if (ran->exclude_flag)
          o << " exclude ";
        else
          o << " from ";

        if (ran->low_open_flag)
          o << "(";
        else
          o << "[";
        if (ran->low_expr)
          o << *ran->low_expr;
        else if (ran->low_open_flag == false)
          o << "-inf";
        else
          o << "<?>";
        if (ran->high_expr)
          o << ":" << *ran->high_expr;
        else if (ran->high_open_flag == false)
          o << ":inf";
        else
          o << ":<?>";
        if (ran->high_open_flag)
          o << ")";
        else
          o << "]";
      }

      o << ";" << endl;
    }
  }

  /* Dump the saved defparam assignments here. */
  {
    list<pair<pform_name_t, PExpr*> >::const_iterator pp;
    for (pp = defparams.begin(); pp != defparams.end(); ++pp) {
      o << "    defparam " << (*pp).first << " = " << *(*pp).second << ";"
        << endl;
    }
  }

  {
    list<pair<list<hname_t>, PExpr*> >::const_iterator pp;
    for (pp = defparams_later.begin(); pp != defparams_later.end(); ++pp) {
      o << "    defparam(later) " << pp->first << " = " << *(pp->second) << ";"
        << endl;
    }
  }

  o << "    enum sets {" << endl;

  /* Dump the enumerations and enum names in this scope. */
  for (map<const enum_type_t*, netenum_t*>::const_iterator cur =
           enum_sets_.begin();
       cur != enum_sets_.end(); ++cur) {
    o << "      " << cur->second << endl;
  }
  o << "    }" << endl;

  o << "    enum names {" << endl;
  for (map<perm_string, NetEConstEnum*>::const_iterator cur =
           enum_names_.begin();
       cur != enum_names_.end(); ++cur) {
    o << "      " << cur->first << " = " << cur->second->value() << " from "
      << cur->second->enumeration() << endl;
  }
  o << "    }" << endl;

  /* Dump the events in this scope. */
  for (NetEvent* cur = events_; cur; cur = cur->snext_) {
    o << "    event " << cur->name() << "; nprobe=" << cur->nprobe()
      << " scope=" << scope_path(cur->scope()) << " // " << cur->get_fileline()
      << endl;
  }

  // Dump the signals,
  for (signals_map_iter_t cur = signals_map_.begin(); cur != signals_map_.end();
       ++cur) {
    cur->second->dump_net(o, 4);
  }

  switch (type_) {
    case FUNC:
      if (func_def())
        func_def()->dump(o, 4);
      else
        o << "    MISSING FUNCTION DEFINITION" << endl;
      break;
    case TASK:
      if (task_def())
        task_def()->dump(o, 4);
      else
        o << "    MISSING TASK DEFINITION" << endl;
      break;
    default:
      break;
  }

  /* Dump any sub-scopes. */
  for (map<hname_t, NetScope*>::const_iterator cur = children_.begin();
       cur != children_.end(); ++cur)
    cur->second->dump(o);

  for (map<perm_string, netclass_t*>::const_iterator cur = classes_.begin();
       cur != classes_.end(); ++cur) {
    cur->second->dump_scope(o);
  }
}

void NetSTask::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << name_;

  if (!parms_.empty()) {
    o << "(";
    if (parms_[0]) parms_[0]->dump(o);

    for (unsigned idx = 1; idx < parms_.size(); idx += 1) {
      o << ", ";
      if (parms_[idx]) parms_[idx]->dump(o);
    }

    o << ")";
  }
  o << ";" << endl;
}

void NetUTask::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << "" << scope_path(task_) << ";" << endl;
}

void NetWhile::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "while (" << *cond_ << ")" << endl;
  proc_->dump(o, ind + 3);
}

/* Dump a statement type that someone didn't write a dump for. */
void NetProc::dump(ostream& o, unsigned ind) const {
  o << setw(ind) << ""
    << "// " << typeid(*this).name() << endl;
}

/* Dump an expression that no one wrote a dump method for. */
void NetExpr::dump(ostream& o) const {
  o << "(?" << typeid(*this).name() << "?)";
}

void NetEAccess::dump(ostream& o) const {
  o << nature_->name() << "." << nature_->access() << "(";
  assert(branch_);
  if (branch_->pin(0).is_linked()) o << branch_->pin(0).nexus()->name();
  o << ", ";
  if (branch_->pin(1).is_linked()) o << branch_->pin(1).nexus()->name();
  o << ")";
}

void NetEArrayPattern::dump(ostream& fd) const {
  fd << "'{";
  if (items_.size() >= 1) {
    if (items_[0]) fd << *items_[0];
  }
  for (size_t idx = 1; idx < items_.size(); idx += 1) {
    fd << ", ";
    if (items_[idx]) fd << *items_[idx];
  }
  fd << "}";
}

void NetEBinary::dump(ostream& o) const {
  if (op_ == 'm' || op_ == 'M') {
    if (op_ == 'm')
      o << "min";
    else
      o << "max";

    o << "(";
    left_->dump(o);
    o << ", ";
    right_->dump(o);
    o << ")";
    return;
  }

  o << "(";
  left_->dump(o);
  o << ")";
  switch (op_) {
    default:
      o << op_;
      break;
    case 'a':
      o << "&&";
      break;
    case 'A':
      o << "~&";
      break;
    case 'e':
      o << "==";
      break;
    case 'E':
      o << "===";
      break;
    case 'w':
      o << "==?";
      break;
    case 'G':
      o << ">=";
      break;
    case 'l':
      o << "<<";
      break;
    case 'L':
      o << "<=";
      break;
    case 'n':
      o << "!=";
      break;
    case 'N':
      o << "!==";
      break;
    case 'W':
      o << "!=?";
      break;
    case 'o':
      o << "||";
      break;
    case 'O':
      o << "~|";
      break;
    case 'p':
      o << "**";
      break;
    case 'r':
      o << ">>";
      break;
    case 'R':
      o << ">>>";
      break;
    case 'X':
      o << "~^";
      break;
  }
  o << "(";
  right_->dump(o);
  o << ")";
}

void NetECast::dump(ostream& fd) const {
  if (op_ == '2')
    fd << "bool<" << expr_width() << ">(" << *expr_ << ")";
  else if (op_ == 'v')
    fd << "logic<" << expr_width() << ">(" << *expr_ << ")";
  else
    NetEUnary::dump(fd);
}

void NetEConcat::dump(ostream& o) const {
  if (repeat_ != 1) o << repeat_;

  if (parms_[0])
    o << "{" << *parms_[0];
  else
    o << "{";

  for (unsigned idx = 1; idx < parms_.size(); idx += 1) {
    if (parms_[idx])
      o << ", " << *parms_[idx];
    else
      o << ", ";
  }
  o << "}";
}

void NetEConst::dump(ostream& o) const {
  if (value_.is_string())
    o << "\"" << value_.as_string() << "\"";
  else
    o << value_;
}

void NetEConstEnum::dump(ostream& o) const {
  o << "<" << name_ << "=";
  NetEConst::dump(o);
  o << ", wid=" << expr_width() << ">";
}

void NetEConstParam::dump(ostream& o) const {
  o << "<" << name_ << "=";
  NetEConst::dump(o);
  o << ", wid=" << expr_width() << ">";
}

void NetECReal::dump(ostream& o) const { o << value_; }

void NetECRealParam::dump(ostream& o) const {
  o << "<" << name_ << "=";
  NetECReal::dump(o);
  o << ">";
}

void NetEEvent::dump(ostream& o) const {
  o << "<event=" << event_->name() << ">";
}

void NetELast::dump(ostream& fd) const {
  fd << "<last of " << sig_->name() << ">";
}

void NetENetenum::dump(ostream& o) const {
  o << "<netenum=" << netenum_ << ">";
}

void NetENew::dump(ostream& o) const {
  o << "new <type> [";
  if (size_) size_->dump(o);
  o << "]";
  if (init_val_) {
    o << "(";
    init_val_->dump(o);
    o << ")";
  }
}

void NetENull::dump(ostream& o) const { o << "<null>"; }

void NetEProperty::dump(ostream& o) const {
  o << net_->name() << ".<" << pidx_ << ">";
  if (index_) o << "[" << *index_ << "]";
}

void NetEScope::dump(ostream& o) const {
  o << "<scope=" << scope_path(scope_) << ">";
}

void NetESelect::dump(ostream& o) const {
  o << "<select";
  if (has_sign())
    o << "+=";
  else
    o << "=";

  expr_->dump(o);
  o << "[";

  if (base_)
    base_->dump(o);
  else
    o << "(0)";

  o << "+:" << expr_width() << "]";
  if (ivl_type_t nt = net_type()) {
    o << " net_type=(" << *nt << ")";
  } else {
    o << " expr_type=" << expr_type();
  }

  o << ">";
}

void NetESFunc::dump(ostream& o) const {
  o << name_ << "(";
  if (nparms() > 0) o << *parm(0);
  for (unsigned idx = 1; idx < nparms(); idx += 1) o << ", " << *parm(idx);
  o << ")";
}

void NetEShallowCopy::dump(ostream& o) const {
  o << "<ShallowCopy, " << *arg1_ << " <-- " << *arg2_ << ">";
}

void NetESignal::dump(ostream& o) const {
  if (has_sign()) o << "+";
  o << name();
  if (word_) o << "[word=" << *word_ << "]";
  vector<netrange_t> tmp = net_->net_type()->slice_dimensions();
  o << tmp;
}

void NetETernary::dump(ostream& o) const {
  o << "(" << *cond_ << ") ? (" << *true_val_ << ") : (" << *false_val_ << ")";
}

void NetEUFunc::dump(ostream& o) const {
  o << scope_path(func_) << "(";
  if (!parms_.empty()) {
    parms_[0]->dump(o);
    for (unsigned idx = 1; idx < parms_.size(); idx += 1) {
      o << ", ";
      parms_[idx]->dump(o);
    }
  }
  o << ")";
}

void NetEUnary::dump(ostream& o) const {
  switch (op_) {
    case 'A':
      o << "~&";
      break;
    case 'm':
      o << "abs";
      break;
    case 'N':
      o << "~|";
      break;
    case 'X':
      o << "~^";
      break;
    case 'I':
      o << "++";
      break;
    case 'D':
      o << "--";
      break;
    case 'i':
    case 'd':
      break;
    default:
      o << op_;
      break;
  }
  o << "(";
  expr_->dump(o);
  o << ")";
  switch (op_) {
    case 'i':
      o << "++";
      break;
    case 'd':
      o << "--";
      break;
  }
}

void Design::dump(ostream& o) const {
  o << "DESIGN TIME PRECISION: 10e" << get_precision() << endl;

  o << "PACKAGES:" << endl;
  for (map<perm_string, NetScope*>::const_iterator cur = packages_.begin();
       cur != packages_.end(); ++cur) {
    cur->second->dump(o);
  }

  o << "SCOPES:" << endl;
  for (list<NetScope*>::const_iterator scope = root_scopes_.begin();
       scope != root_scopes_.end(); ++scope) {
    (*scope)->dump(o);
  }

  o << "ELABORATED NODES:" << endl;

  // dump the nodes,
  if (nodes_) {
    NetNode* cur = nodes_->node_next_;
    do {
      assert(cur);
      cur->dump_node(o, 0);
      cur = cur->node_next_;
    } while (cur != nodes_->node_next_);
  }

  o << "ELABORATED BRANCHES:" << endl;

  if (branches_) {
    for (NetBranch* cur = branches_; cur; cur = cur->next_) cur->dump(o, 0);
  }

  o << "ELABORATED PROCESSES:" << endl;

  // Dump the processes.
  for (const NetProcTop* idx = procs_; idx; idx = idx->next_) idx->dump(o, 0);

  for (const NetAnalogTop* idx = aprocs_; idx; idx = idx->next_)
    idx->dump(o, 0);
}
